﻿// <copyright file="Persistance.cs" company="BerlinSoft">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Wernfried Schwenkner</author>
// <email>wernfried@schwenkner.de</email>
// <date>2009-10-21</date>
// <summary>
// Contains the Persistance class.
// </summary>

using System;
using System.Data.SqlServerCe;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using SenseTimeTracker.BusinessLayer;
using SenseTimeTracker.Export;
using SenseTimeTracker.Properties;
using SenseTimeTracker.TimeCards;

namespace SenseTimeTracker.Misc
{

	/// <summary>
	/// Possible languages to be selected
	/// </summary>
	public enum AvailableLanguage
	{
		/// <summary>
		/// English language is selected
		/// </summary>
		[Description("Language_en")]
		en,

		/// <summary>
		/// German language is selected
		/// </summary>
		[Description("Language_de")]
		de,

		/// <summary>
		/// Dutch language is selected
		/// </summary>
		[Description("Language_nl")]
		nl,
	}

	/// <summary>
	/// Possible sort order for tables
	/// </summary>
	public enum SortOrder
	{
		/// <summary>
		/// Sort by ascending order
		/// </summary>
		[Description("SortOrderAscending")]
		Ascending,

		/// <summary>
		/// Sort by descending order
		/// </summary>
		[Description("SortOrderDescending")]
		Descending,

		/// <summary>
		/// Do not sort
		/// </summary>
		[Description("SortOrderUnsorted")]
		Unsorted
	}

	/// <summary>
	/// Possible sort order for tables
	/// </summary>
	[Flags]
	public enum SortColumn
	{
		/// <summary>
		/// Sort by name column
		/// </summary>
		[Description("SortColumnName")]
		Name = 0x0001,

		/// <summary>
		/// Sort by type column
		/// </summary>
		[Description("SortColumnType")]
		Type = 0x0002,

		/// <summary>
		/// Sort by number column
		/// </summary>
		[Description("SortColumnNumber")]
		Number = 0x0004,

		/// <summary>
		/// Sort by date column
		/// </summary>
		[Description("SortColumnDate")]
		Date = 0x0008,

		/// <summary>
		/// Sort by description column
		/// </summary>
		[Description("SortColumnDescription")]
		Description = 0x0010,

		/// <summary>
		/// Sort by duration columb
		/// </summary>
		[Description("SortColumnDuration")]
		Duration = 0x0020,

		/// <summary>
		/// Sort by estimate column
		/// </summary>
		[Description("SortColumnEstimate")]
		Estimate = 0x0040,
	}

	public enum ReportType
	{
		[Description("ReportTypeTotal")]
		[ReportLevel(0)]
		Total,

		[Description("ReportTypeProjectRelated")]
		[ReportLevel(0)]
		Project,

		[Description("ReportTypeUtilisation")]
		[ReportLevel(0)]
		Utilisation,

		[Description("ReportTypeUtilisationByClient")]
		[ReportLevel(1)]
		UtilisationByClient,

		[Description("ReportTypeUtilisationByProject")]
		[ReportLevel(1)]
		UtilisationByProject,

		[Description("ReportTypeUtilisationByTask")]
		[ReportLevel(1)]
		UtilisationByTask,
	}

	/// <summary>
	/// Time tracker persistance class
	/// </summary>
	[XmlRoot("TimeTracker")]
	public class Persistance
	{
		/// <summary>
		/// Field contains the config file name
		/// </summary>
		[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1303:ConstFieldNamesMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")]
		private const string configFile = "TimeTracker.xml";

		/// <summary>
		/// Field contains the application path
		/// </summary>
		private static readonly string applicationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName) + Path.DirectorySeparatorChar;

		/// <summary>
		/// Instance of the singleton
		/// </summary>
		private static Persistance instance;

		/// <summary>
		/// Field contains a value indication whether to prevent serialisation
		/// </summary>
		private static bool preventSerialization;

		/// <summary>
		/// Field contains value indication whether the userId is prooved
		/// </summary>
		private bool userIdProoved;

		/// <summary>
		/// Field contains the colors collection
		/// </summary>
		private Colors colors = new Colors();

		/// <summary>
		/// Field contains the clients sorting
		/// </summary>
		private Sorting clientsSorting = new Sorting();

		/// <summary>
		/// Field contains the categories sorting
		/// </summary>
		private Sorting categoriesSorting = new Sorting();

		/// <summary>
		/// Field contains the holidays sorting
		/// </summary>
		private Sorting holidaysSorting = new Sorting();

		/// <summary>
		/// Field contains the projects sorting
		/// </summary>
		private Sorting projectsSorting = new Sorting();

		/// <summary>
		/// Field contains the tasks sorting
		/// </summary>
		private Sorting tasksSorting = new Sorting();

		/// <summary>
		/// Field contains the users sorting
		/// </summary>
		private Sorting usersSorting = new Sorting();

		/// <summary>
		/// Field contains the default final time
		/// </summary>
		private DateTime defaultFinalTime = DateTime.Parse("17:00");

		/// <summary>
		/// Field contains the default start time
		/// </summary>
		private DateTime defaultStartTime = DateTime.Parse("9:00");

		/// <summary>
		/// Field contains the defaul daily working time
		/// </summary>
		private TimeSpan defaultDailyWorkingTime = TimeSpan.FromHours(8.0);

		/// <summary>
		/// Field contains the time cards sorting
		/// </summary>
		private Sorting timeCardsSorting = new Sorting { Column = SortColumn.Date };

		/// <summary>
		/// Field contains the user id
		/// </summary>
		private Guid userId = Guid.Empty;

		/// <summary>
		/// Field contains the lastly selected client
		/// </summary>
		private Guid selectedClient = DatabaseManager.AllClientsId;

		/// <summary>
		/// Field contains the lastly selected project
		/// </summary>
		private Guid selectedProject = DatabaseManager.AllProjectsId;

		/// <summary>
		/// Field contains the lastly selected task
		/// </summary>
		private Guid selectedTask = DatabaseManager.AllTasksId;

		/// <summary>
		/// Field contains the lastly selected new client
		/// </summary>
		private Guid selectedNewClient = DatabaseManager.AllClientsId;

		/// <summary>
		/// Field contains the lastly selected new project
		/// </summary>
		private Guid selectedNewProject = DatabaseManager.AllProjectsId;

		/// <summary>
		/// Field contains the lastly selected new task
		/// </summary>
		private Guid selectedNewTask = DatabaseManager.AllTasksId;

		/// <summary>
		/// Field contains the lastly selected export type
		/// </summary>
		private ExportForm.ExportType exportType = ExportForm.ExportType.XML;

		/// <summary>
		/// Field contains the lastly selected export period
		/// </summary>
		private ExportForm.ExportPeriod exportPeriod = ExportForm.ExportPeriod.Week;

		/// <summary>
		/// Field contains the location of the database
		/// </summary>
		private string databaseLocation = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + Path.DirectorySeparatorChar + "SenseTimeTracker" + Path.DirectorySeparatorChar + DatabaseManager.DatabaseName;

		/// <summary>
		/// Field contains the location of the backup database
		/// </summary>
		private string backupDatabaseLocation = string.Empty;

		private TimeCardFilter filterForTimeCards = new TimeCardFilter();
		private ProjectFilter filterForProjects = new ProjectFilter();
		private TaskFilter filterForTasks = new TaskFilter();
		private ReportType reportType = ReportType.Total;
		private CultureInfo selectedLanguage = CultureInfo.CurrentCulture;

		private bool useTextualDateTime = false;

		/// <summary>
		/// Prevents a default instance of the <see cref="Persistance"/> class from being created.
		/// </summary>
		private Persistance()
		{
		}

		public class Sorting
		{

			private SortColumn sortColumn = SortColumn.Name;
			private SortOrder sortOrder = SortOrder.Ascending;

			[XmlAttribute("Column")]
			public SortColumn Column
			{
				get { return sortColumn; }
				set { sortColumn = value; }
			}

			[XmlAttribute("Order")]
			public SortOrder Order
			{
				get { return sortOrder; }
				set { sortOrder = value; }
			}
		}

		public class TimeCardFilter
		{
			private Guid clientFilter = DatabaseManager.AllClientsId;
			private Guid projectFilter = DatabaseManager.AllProjectsId;
			private Guid taskFilter = DatabaseManager.AllTasksId;

			/// <summary>
			/// Field contains the period filter value
			/// </summary>
			private TimeCardsSliderPanel.PeriodFilter periodFilter = TimeCardsSliderPanel.PeriodFilter.ThisMonth;

			public Guid ClientFilter
			{
				get { return clientFilter; }
				set { clientFilter = value; }
			}

			public Guid ProjectFilter
			{
				get { return projectFilter; }
				set { projectFilter = value; }
			}

			public Guid TaskFilter
			{
				get { return taskFilter; }
				set { taskFilter = value; }
			}

			/// <summary>
			/// Gets or sets the period filter
			/// </summary>
			[XmlElement("RecordFilter")]
			[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
			public TimeCardsSliderPanel.PeriodFilter PeriodFilter
			{
				get { return periodFilter; }
				set { periodFilter = value; }
			}
		}

		public class ProjectFilter
		{
			private Guid clientFilter = DatabaseManager.AllClientsId;

			[XmlAttribute("Client")]
			public Guid ClientFilter
			{
				get { return clientFilter; }
				set { clientFilter = value; }
			}
		}

		public class TaskFilter
		{
			private Guid clientFilter = DatabaseManager.AllClientsId;
			private Guid projectFilter = DatabaseManager.AllProjectsId;
			private bool displayFinishedTasks = false;

			[XmlAttribute("Client")]
			public Guid ClientFilter
			{
				get { return clientFilter; }
				set { clientFilter = value; }
			}

			/// <summary>
			/// Gets or sets the projact filter
			/// </summary>
			[XmlAttribute("Project")]
			public Guid ProjectFilter
			{
				get { return projectFilter; }
				set { projectFilter = value; }
			}

			/// <summary>
			/// Gets or sets a value indicating whether to display finished tasks
			/// </summary>
			[XmlAttribute("DisplayFiishedTasks")]
			public bool DisplayFinishedTasks
			{
				get { return displayFinishedTasks; }
				set { displayFinishedTasks = value; }
			}
		}

		/// <summary>
		/// Gets the instance of that class
		/// </summary>
		public static Persistance Instance
		{
			get
			{
				if (instance == null)
				{
					instance = Deserialize();
				}

				return instance;
			}
		}

		[XmlElement("FilterForTimeCards")]
		public TimeCardFilter FilterForTimeCards
		{
			get { return filterForTimeCards; }
			set { filterForTimeCards = value; }
		}

		[XmlElement("FilterForProjects")]
		public ProjectFilter FilterForProjects
		{
			get { return filterForProjects; }
			set { filterForProjects = value; }
		}

		[XmlElement("FilterForTasks")]
		public TaskFilter FilterForTasks
		{
			get { return filterForTasks; }
			set { filterForTasks = value; }
		}

		[XmlElement("ReportType")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public ReportType ReportType
		{
			get { return reportType; }
			set { reportType = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the default final time
		/// </summary>
		[XmlIgnore]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public DateTime DefaultFinalTime
		{
			get { return defaultFinalTime; }
			set { defaultFinalTime = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the default final time
		/// </summary>
		[XmlAttribute("DefaultFinalTime")]
		public string DefaultFinalTimeAsString
		{
			get { return defaultFinalTime.TimeOfDay.ToString(); }
			set { defaultFinalTime = DateTime.Now.Date + TimeSpan.Parse(value); }
		}

		/// <summary>
		/// Gets or sets the default start time
		/// </summary>
		[XmlIgnore]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public DateTime DefaultStartTime
		{
			get { return defaultStartTime; }
			set { defaultStartTime = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the default start time as a string
		/// </summary>
		[XmlAttribute("DefaultStartTime")]
		public string DefaultStartTimeAsString
		{
			get { return defaultStartTime.TimeOfDay.ToString(); }
			set { defaultStartTime = DateTime.Now.Date + TimeSpan.Parse(value); }
		}

		/// <summary>
		/// Gets or sets the default daily working time.
		/// </summary>
		[XmlIgnore]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public TimeSpan DefaultDailyWorkingTime
		{
			get { return defaultDailyWorkingTime; }
			set { defaultDailyWorkingTime = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the default daily working time as double
		/// </summary>
		[XmlAttribute("DefaultDailyWorkingTime")]
		public double DefaultDailyWorkingTimeAsDouble
		{
			get { return defaultDailyWorkingTime.TotalHours; }
			set { defaultDailyWorkingTime = TimeSpan.FromHours(value); }
		}

		/// <summary>
		/// Gets or sets the selected client
		/// </summary>
		[XmlElement("SelectedClient")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public Guid SelectedClient
		{
			get { return selectedClient; }
			set { selectedClient = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the selected project
		/// </summary>
		[XmlElement("SelectedProject")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public Guid SelectedProject
		{
			get { return selectedProject; }
			set { selectedProject = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the selected task
		/// </summary>
		[XmlElement("SelectedTask")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public Guid SelectedTask
		{
			get { return selectedTask; }
			set { selectedTask = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the users sorting
		/// </summary>
		[XmlElement("UsersSorting")]
		public Sorting UsersSorting
		{
			get { return usersSorting; }
			set { usersSorting = value; }
		}

		/// <summary>
		/// Gets or sets the clients sorting
		/// </summary>
		[XmlElement("ClientsSorting")]
		public Sorting ClientsSorting
		{
			get { return clientsSorting; }
			set { clientsSorting = value; }
		}

		/// <summary>
		/// Gets or sets the categories sorting
		/// </summary>
		[XmlElement("CategoriesSorting")]
		public Sorting CategoriesSorting
		{
			get { return categoriesSorting; }
			set { categoriesSorting = value; }
		}

		/// <summary>
		/// Gets or sets the holidays sorting
		/// </summary>
		[XmlElement("HolidaysSorting")]
		public Sorting HolidaysSorting
		{
			get { return holidaysSorting; }
			set { holidaysSorting = value; }
		}

		/// <summary>
		/// Gets or sets the projects sorting
		/// </summary>
		[XmlElement("ProjectsSorting")]
		public Sorting ProjectsSorting
		{
			get { return projectsSorting; }
			set { projectsSorting = value; }
		}

		/// <summary>
		/// Gets or sets the tasks sorting
		/// </summary>
		[XmlElement("TasksSorting")]
		public Sorting TasksSorting
		{
			get { return tasksSorting; }
			set { tasksSorting = value; }
		}

		/// <summary>
		/// Gets or sets the time records sorting
		/// </summary>
		[XmlElement("TimeCardsSorting")]
		public Sorting TimeRecordsSorting
		{
			get { return timeCardsSorting; }
			set { timeCardsSorting = value; }
		}

		/// <summary>
		/// Gets or sets the database loaction
		/// </summary>
		[XmlElement("Database")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public string DatabaseLocation
		{
			get { return databaseLocation; }
			set { databaseLocation = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the backup database location
		/// </summary>
		[XmlElement("BackupDatabase")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public string BackupDatabaseLocation
		{
			get { return backupDatabaseLocation; }
			set { backupDatabaseLocation = value; Serialize(); }
		}

		///// <summary>
		///// Gets or sets the selected task
		///// </summary>
		//[XmlElement("SelectedTask")]
		//public Guid SelectedTask
		//{
		//  get { return selectedTask; }
		//  set { selectedTask = value; }
		//}

		/// <summary>
		/// Gets or sets the export type
		/// </summary>
		[XmlAttribute("ExportType")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public ExportForm.ExportType ExportType
		{
			get { return exportType; }
			set { exportType = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the export period
		/// </summary>
		[XmlAttribute("ExportPeriod")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public ExportForm.ExportPeriod ExportPeriod
		{
			get { return exportPeriod; }
			set { exportPeriod = value; Serialize(); }
		}

		/// <summary>
		/// Gets or sets the current selected langugae
		/// </summary>
		[XmlIgnore]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public CultureInfo SelectedLanguage
		{
			get { return selectedLanguage; }
			set { selectedLanguage = value; Serialize(); Resources.Culture = value; }
		}

		/// <summary>
		/// Gets or sets the current selected langugae
		/// </summary>
		[XmlAttribute("Language")]
		[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "can contain serialize")]
		public string Language
		{
			get { return selectedLanguage.Name; }
			set { selectedLanguage = CultureInfo.CreateSpecificCulture(value); Resources.Culture = selectedLanguage; }
		}

		/// <summary>
		/// Gets or sets the colors collection
		/// </summary>
		[XmlElement("Colors")]
		public Colors Colors
		{
			get { return colors; }
			set { colors = value; }
		}

		/// <summary>
		/// Gets or sets the user id.
		/// It checks if the user id is available.
		/// </summary>
		[XmlElement("CurrentUser")]
		public Guid UserId
		{
			get
			{
				if (!userIdProoved && userId != Guid.Empty)
				{
					User user = User.GetUser(userId);
					userId = user != null ? user.Id : Guid.Empty;

					SqlCeCommand command = new SqlCeCommand("SELECT personId FROM Persons WHERE personId = @personId", DatabaseManager.DbConnection);
					command.Parameters.Add("@personId", userId);
					DatabaseManager.DbConnection.Open();
					object userIdToBeTested = command.ExecuteScalar();
					DatabaseManager.DbConnection.Close();
					userId = userIdToBeTested is Guid ? (Guid)userIdToBeTested : Guid.Empty;
					userIdProoved = true;
				}

					return userId;
			}

			set
			{
				userId = value;
				Serialize();
			}
		}

		public bool UseTextualDateTime
		{
			get { return useTextualDateTime; }
			set { useTextualDateTime = value; Serialize(); }
		}

		/// <summary>
		/// Serializes the configuration
		/// </summary>
		public void Serialize()
		{
			if (!preventSerialization)
			{
				XmlSerializer serializer = new XmlSerializer(typeof(Persistance));
				TextWriter textWriter = new StreamWriter(applicationPath + configFile);

				serializer.Serialize(textWriter, this);
				textWriter.Close();
			}
		}

		/// <summary>
		/// Deserializes the configuration
		/// </summary>
		/// <returns>An instance of the persistant configuration</returns>
		private static Persistance Deserialize()
		{
			Persistance tracker = new Persistance();
			if (File.Exists(applicationPath + configFile))
			{
				XmlSerializer serializer = new XmlSerializer(typeof(Persistance));
				TextReader textReader = null;
				try
				{
					preventSerialization = true;
					textReader = new StreamReader(applicationPath + configFile);
					tracker = (Persistance)serializer.Deserialize(textReader);
					textReader.Close();
				}
				catch
				{
					tracker = new Persistance();
					if (textReader != null)
					{
						textReader.Close();
					}

					preventSerialization = false;
					File.Delete(configFile);
					tracker.Serialize();
				}
			}

			preventSerialization = false;
			return tracker;
		}
	}
}
